using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Windows.Forms;
using System.Threading;

namespace LogAnalyzer.Grabbers
{

    public delegate void NewStringIncomingHandler(object sender, NewStringIncomingEventArgs e);
    public delegate void ClearViewerHandler();


    public abstract class GrabberBase : IDisposable
    {
        private const int FIRE_NEW_STRING_EVENT_MAX_ROWS_BLOCK_SIZE = 500;

        private List<GrabFilter> _grabFilters;
        private Dictionary<string, string> _config;
        private string _grabberType;
        private string _buffer;


        #region Events

        public event NewStringIncomingHandler NewStringIncoming;
        public event NewStringIncomingHandler NewStringIncomingUnfiltered;
        public event ClearViewerHandler ClearViewer;

        #endregion

        #region Properties

        public List<GrabFilter> GrabFilters
        {
            get
            {
                if (_grabFilters == null)
                {
                    _grabFilters = new List<GrabFilter>();
                }
                if (_grabFilters.Count == 0)
                {
                    GrabFilter defaultFilter = new GrabFilter();
                    defaultFilter.QueryString = "*";
                    defaultFilter.MatchAction = GrabAction.Accept;
                    defaultFilter.AfterMatchAction = GrabAfterMatchAction.GoOn;
                    _grabFilters.Add(defaultFilter);
                }
                return _grabFilters;
            }
        }

        public Dictionary<string, string> Config
        {
            get 
            {
                if (_config == null)
                    _config = new Dictionary<string, string>();
                return _config;
            }
        }

        private string GrabberType
        {
            get
            {
                if (_grabberType == null)
                {
                    _grabberType = this.GetType().ToString();
                }

                return _grabberType;
            }
        }

        public abstract string TextForViewer { get; }

        #endregion

        #region Methods

        public abstract DialogResult ShowConfigDialog();
        protected abstract void StartGrab(bool isRestart);
        public abstract void StopGrab();

        public void LoadConfig(XmlNode nodeGrabber)
        {
            foreach (XmlNode cfgItemNode in (nodeGrabber.SelectNodes("cfg/item")))
            {
                Config.Add(
                    cfgItemNode.Attributes["key"].Value,
                    cfgItemNode.Attributes["value"].Value);
            }

            _grabFilters = new List<GrabFilter>();
            foreach (XmlNode nodeFilter in nodeGrabber.SelectNodes("filters/filter"))
            {
                GrabFilter filter = new GrabFilter();
                filter.LoadConfig(nodeFilter);
                _grabFilters.Add(filter);
            }
        }

        public void SaveConfig(XmlNode nodeGrabber)
        {
            XmlDocument xml = nodeGrabber.OwnerDocument;
            nodeGrabber.Attributes.Append(xml.CreateAttribute("type"));
            nodeGrabber.Attributes["type"].Value = GrabberType;
       
            XmlNode cfgNode =  xml.CreateElement("cfg");
            nodeGrabber.AppendChild(cfgNode);
            
            foreach (string key in _config.Keys)
            {
                XmlNode itemNode = xml.CreateElement("item");
                itemNode.Attributes.Append(xml.CreateAttribute("key"));
                itemNode.Attributes.Append(xml.CreateAttribute("value"));
                itemNode.Attributes["key"].Value = key;
                itemNode.Attributes["value"].Value = _config[key];
                cfgNode.AppendChild(itemNode);
            }

            XmlNode nodeFilters = xml.CreateElement("filters");
            nodeGrabber.AppendChild(nodeFilters);
            foreach (GrabFilter filter in GrabFilters)
            {
                XmlNode nodeFilter = xml.CreateElement("filter");
                filter.SaveConfig(nodeFilter);
                nodeFilters.AppendChild(nodeFilter);
            }
        }
             
        public void StartGrab()
        {
            new Thread(delegate() {
                try { StartGrab(false); }
                catch (Exception ex) { FireNewStringIncomingUnfiltered("START ERROR: " + ex.Message + "\n"); }
            }).Start();
        }
        
        public void RestartGrab()
        {
            StopGrab();
            _buffer = "";

            new Thread(delegate()
            {
                try { StartGrab(true); }
                catch (Exception ex) { FireNewStringIncomingUnfiltered("START ERROR: " + ex.Message + "\n"); }
            }).Start();
        }

        protected internal void HandleNewString(string s)
        {
            /// this method collects all strings incoming from grabbers and divide
            /// them in rows, applies the grab filters and fire event for each line
            /// 
            if (NewStringIncoming == null && NewStringIncomingUnfiltered == null)
                return;

            if (s == null)
                s = "";

            /// grab filter
            /// 
            _buffer += s;
            int lfIdx = _buffer.LastIndexOf('\n');
            if (lfIdx == -1)
            {
                /// not a full line of string is arrived
                /// 
                return;
            }

            bool lfIsLast = (lfIdx == (_buffer.Length - 1));
            string incomingString = _buffer.Substring(0, lfIdx);
            string[] rows = incomingString.Split('\n');
            if (lfIsLast)
            {
                _buffer = "";
            }
            else
            {
                _buffer = _buffer.Substring(lfIdx + 1);
            }


            string incomingStringToThrow = "";
            string incomingStringUnfilteresToThrow = "";

            int grabbedRowsCount = 0;
            int rowIdx = -1;
            foreach (string r in rows)
            {
                string row = r.TrimEnd('\r');
                rowIdx++;


                /// check grab filters on each row
                /// 
                bool grabLine = false;


                foreach (GrabFilter filter in GrabFilters)
                {
                    if (filter.InnerRegEx.IsMatch(row))
                    {
                        switch (filter.MatchAction)
                        {
                            case GrabAction.Accept: grabLine = true; break;
                            case GrabAction.Discard: grabLine = false; break;
                        }
                        if (filter.AfterMatchAction == GrabAfterMatchAction.StopHere)
                        {
                            break;
                        }
                    }
                }

                row += "\r\n";
                grabbedRowsCount++;


                incomingStringUnfilteresToThrow += row;


                if (grabLine)
                {
                    incomingStringToThrow += row;
                }



                if (grabbedRowsCount % FIRE_NEW_STRING_EVENT_MAX_ROWS_BLOCK_SIZE == 0
                    || rowIdx == (rows.Length - 1))
                {

                    // fire new string unfiltered event
                    FireNewStringIncomingUnfiltered(incomingStringUnfilteresToThrow);
                    incomingStringUnfilteresToThrow = "";


                    // fire new string event
                    if (incomingStringToThrow.Length > 0)
                    {
                        FireNewStringIncoming(incomingStringToThrow);
                        incomingStringToThrow = "";
                    }
                    
                }

            }


        }

        private void FireNewStringIncoming(string row)
        {
            if (NewStringIncoming != null)
            {
                new Thread(delegate()
                {
                    NewStringIncoming(this, new NewStringIncomingEventArgs(row));
                }).Start();
            }
        }
        private void FireNewStringIncomingUnfiltered(string row)
        {
            if (NewStringIncomingUnfiltered != null)
            {
                new Thread(delegate()
                {
                    NewStringIncomingUnfiltered(this, new NewStringIncomingEventArgs(row));
                }).Start();
            }
        }        
        protected internal void FireClearViewerEvent()
        {
            if (ClearViewer != null)
            {
                new Thread(delegate()
                {
                    ClearViewer();
                }).Start();
            }
        }

        #endregion


        #region IDisposable Members

        public abstract void Dispose();

        #endregion

    }

    public enum DataEncoding
    {
        ASCII,
        BigEndianUnicode,
        Unicode,
        UTF32,
        UTF7,
        UTF8
    }
}
